﻿using System;
using System.Security.Cryptography;
using System.Text;

namespace NMA.Infrastructure.Security
{
    /// <summary>
    /// This utility class provides methods for hashing strings.
    /// </summary>
    public class StringHasher : ICryptography, IDisposable
    {
        #region Private Members

        private readonly HashAlgorithm _algorithm;
        private bool _disposed = false;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new string hasher.
        /// </summary>
        public StringHasher()
        {
            _algorithm = new SHA256Managed();
        }

        #endregion

        #region Finalizer / Dispose Pattern

        /// <summary>
        /// Finalizer for class that is called by garabage collector.  To implement
        /// the dispose pattern correctly we just call the Dispose method specifying
        /// this is not a call form user code.
        /// </summary>
        ~StringHasher()
        {
            Dispose(false);
        }

        /// <summary>
        /// Version of dispose that will be called by clients (or implicitly if code
        /// is used in a using block).
        /// Calls the internal disposer and removes the object from the finalization
        /// queue.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Does the actual work of disposal.
        /// </summary>
        /// <param name="disposing">If true, we are being called from user code.  If false
        /// we are being called from the garbage collector.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // Dispose managed resources.  We can't call the algorithm's dispose
                    // but we can call clear which should have the same effect.
                    _algorithm.Clear();
                }
            }

            _disposed = true;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Hashes the given cleartext string.
        /// </summary>
        /// <param name="cleartext">String to hash.</param>
        /// <returns>Hashed version of the string.</returns>
        public string HashString(string cleartext)
        {
            string hashedString;

            if (String.IsNullOrEmpty(cleartext))
            {
                hashedString = String.Empty;
            }
            else
            {
                // Convert the string to a byte array.
                UnicodeEncoding encoding = new UnicodeEncoding();
                byte[] clearbytes = encoding.GetBytes(cleartext);
                byte[] hash = _algorithm.ComputeHash(clearbytes);

                // Convert the hash back into a string for easy storage and
                // comparison.
                // We use base64 to guarantee printability.
                hashedString = Convert.ToBase64String(hash);
            }

            return hashedString;
        }

        #endregion
    }
}